/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */


package cn2;

import java.util.*;
import java.net.*;
import java.io.*;
import java.nio.ByteBuffer;


/**
 *
 * @author michaellangmayr
 */
public class RemotePeer implements Serializable {
    private String IP;
    private int port;
    long timestamp;
    private boolean isPause = false;
    //private Socket s;


    
    public RemotePeer(String IP, int port){
        this.IP = IP;
        this.port = port;
    }

    public String getIP() {
        return this.IP;        
    }

    public int getPort() {
        return this.port;
    }

    private void refreshTimestamp() {
        this.timestamp = System.currentTimeMillis();
    }

    public boolean isTimeout() {

        // timeout occurs after 10 minutes
        if ( (this.timestamp - System.currentTimeMillis()) > 600000) {
            return true;
        } else {
            return false;
        }
    }

    public byte[] getFile(String filename, long size, long offset, javax.swing.JProgressBar pBar) throws Exception {

        int off = 0;
        int len = 1024;
        int retLen = 0;
        int tmpLen = 0;
        byte[] b = new byte[(int)size + 1024];
        
        System.out.println("[RemotePeer] downloading file " + filename + "(" + (int)size + ") goes to " + b.length + "with offset " + offset);

        try {
            Socket s = this.createSocket();

            // Send Command
            Command cmd = new Command();
            cmd.setCmdEnum(CmdEnum.GET);
            cmd.setPara1(filename);
            cmd.setPara2(String.valueOf(offset));
            
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(bos);

            out.writeObject(cmd);

            OutputStream os = s.getOutputStream();
            os.write(bos.toByteArray());
            
            InputStream i = s.getInputStream();

            while( (tmpLen = i.read(b, off, len)) != -1 ) {

                System.out.println("[RemotePeer] rcv chunk " + off + " " + tmpLen);
                
                off = off + tmpLen;
                System.out.println("[RemotePeer] next offset: " + off);

                // update progress bar
                pBar.setValue(((int)offset)+off);

                retLen = retLen + tmpLen;

                if(this.isPause()) {
                    break;
                }
            }           

            if (retLen <= 0) {
                throw new Exception("File has length 0");
            }

            System.out.println("[RemotePeer] downloaded " + retLen + " bytes");

            this.closeSocket(s);

        } catch (Exception e) {
            System.out.println("[RemotePeer] exception while rcving file: " + e.toString());
            throw e;
        }
        byte[] resultBytes = new byte[retLen];

        System.arraycopy(b, 0, resultBytes, 0, retLen);
        return resultBytes;
    }

    public boolean isAlive() {

        System.out.println("[RemotePeer] pinging " + this.toString() + "...");

        boolean isAlive = false;

        try {
            Socket s = this.createSocket();
            s.setSoTimeout(10000);

            // Send Command
            Command cmd = new Command();
            cmd.setCmdEnum(CmdEnum.ALIVE);            

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(bos);

            out.writeObject(cmd);

            OutputStream os = s.getOutputStream();
            os.write(bos.toByteArray());
                        
            BufferedReader bin = new BufferedReader(new InputStreamReader(s.getInputStream()));
            String rcvStr = bin.readLine();
            
            if (rcvStr.equals("OK")) {
                System.out.println("[RemotePeer] isalive " + this.toString() + " OK...");
                isAlive = true;

            } else {
                System.out.println("[RemotePeer] isalive " + this.toString() + " NOK...");
                isAlive = false;
            }

            this.closeSocket(s);
            
        } catch (Exception e) {
            System.out.println("[RemotePeer] exception while rcving isalive answer: " + e.toString());
            return false;
        }
        
        return isAlive;        
    }

    public ArrayList getNeighbours() throws Exception {

        System.out.println("[RemotePeer] getting neighbours from remotepeer: " + this.toString());
        System.out.println("[RemotePeer] creating socket...");
        Socket s = this.createSocket();
        
        ArrayList neighbours = new ArrayList();

        // Send Command
        Command cmd = new Command();
        cmd.setCmdEnum(CmdEnum.LISTNEIGHBOURS);        

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);

        out.writeObject(cmd);

        OutputStream os = s.getOutputStream();
        os.write(bos.toByteArray());               

        // Receive List of Neighbours
        System.out.println("[RemotePeer] receiving list...");
        try {
            ObjectInputStream is = new ObjectInputStream(s.getInputStream());
            neighbours = (ArrayList)is.readObject();
        } catch (Exception e) {
            System.out.println("[RemotePeer] exception while rcving neighbours: " + e.toString());
        }
        
        this.closeSocket(s);

        System.out.println("[RemotePeer] received neighbours (" + neighbours.size() + "):");
        Iterator it = neighbours.iterator();
        while(it.hasNext()) {
            System.out.println(((RemotePeer)it.next()).toString());
        }

        return neighbours;
    }

    public Socket createSocket() throws Exception {
        return new Socket(IP, port);
    }

    public void closeSocket(Socket s) throws Exception {
        s.close();
    }

    public boolean equals(RemotePeer rp2) {
        if(this.IP.equals(rp2.IP) && this.port == rp2.port) {
            return true;
        }

        return false;
    }

    public void registerAsNeighbour(int myPort) throws Exception {
        
        Socket s = this.createSocket();        

        // Send Command
        Command cmd = new Command();
        cmd.setCmdEnum(CmdEnum.REGISTERASNEIGHBOUR);
        cmd.setPara1(String.valueOf(myPort));

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);

        out.writeObject(cmd);

        OutputStream os = s.getOutputStream();
        os.write(bos.toByteArray());        
        this.closeSocket(s);
    }

    public void unregisterAsNeighbour(int myPort) throws Exception {
          Socket s = this.createSocket();

        // Send Command
        Command cmd = new Command();
        cmd.setCmdEnum(CmdEnum.UNREGISTERASNEIGHBOUR);
        cmd.setPara1(String.valueOf(myPort));

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);

        out.writeObject(cmd);

        OutputStream os = s.getOutputStream();
        os.write(bos.toByteArray());
        this.closeSocket(s);
    }

    public ArrayList search(String arg, int myPort, String myAddr, int randomID) throws Exception {
        Socket s = this.createSocket();
        
        ArrayList results = new ArrayList();       

        // Send Command
        Command cmd = new Command();
        cmd.setCmdEnum(CmdEnum.SEARCH);
        cmd.setPara1(String.valueOf(myPort));
        cmd.setPara2(arg);
        cmd.setPara3(String.valueOf(randomID));
        cmd.setPara4(myAddr);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);

        out.writeObject(cmd);

        OutputStream os = s.getOutputStream();
        os.write(bos.toByteArray());

        // Receive search results
        System.out.println("[RemotePeer] receiving search results...");
        try {
            ObjectInputStream is = new ObjectInputStream(s.getInputStream());
            results = (ArrayList)is.readObject();
        } catch (Exception e) {
            System.out.println("[RemotePeer] exception while rcving search results: " + e.toString());
        }

        this.closeSocket(s);

        System.out.println("[RemotePeer] received results: ");
        Iterator it = results.iterator();
        while (it.hasNext()) {

            SearchResult tmpResult = (SearchResult)it.next();
            System.out.println(tmpResult.getFilename() + "on " + tmpResult.getRemotePeer().toString());
        }

        return results;
    }

    @Override
    public String toString() {
        return this.IP + ":" + this.port;
    }

    @Override
    public boolean equals(Object o) {
        RemotePeer tmpPeer = (RemotePeer)o;
        if ( (tmpPeer.getIP().compareTo(this.getIP()) == 0) && (tmpPeer.getPort() == this.getPort())) {
            return true;
        } else {
            return false;
        }
    }

    public boolean isPause() {
        return this.isPause;
    }
    public void togglePause() {

        if (this.isPause == true) {
            this.isPause = false;
        } else {
            this.isPause = true;
        }        
    }    
}
